package icraf.carbon.gui;

import static icraf.carbon.gui.MenuFactory.MenuProperty.*;
import static icraf.carbon.gui.MenuFactory.MenuProperty.MODEL_LANDUSE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.MODEL_COSTB_TOTAL;
import static icraf.carbon.gui.MenuFactory.MenuProperty.MODEL_OPCOST;
import static icraf.carbon.gui.MenuFactory.MenuProperty.MODEL_SIMULATION;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_ZONATION;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_BG_EMISSION;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_ABATEMENT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_COSTB;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_COSTB_UNIT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_CSTOCK;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_ECONOMIC;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_EMISSION_ADD;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_INPUT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_LANDUSE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_LCOVER_CHANGE;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_MANAGER;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_NPV;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_OUTPUT;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_SPATIAL;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_SUMMARY;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_SUMMARY_OPCOST;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_SUMMARY_EMISSION;
import static icraf.carbon.gui.MenuFactory.MenuProperty.PROJECT_COSTB_TOTAL;
import icraf.carbon.core.Project;
import icraf.carbon.gui.MenuFactory.MenuProperty;
import icraf.common.gui.MenuAdapter;
import icraf.common.gui.MenuGenerator.IconModel;

import java.awt.Component;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;

import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.KeyStroke;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import javax.swing.tree.TreeSelectionModel;

import com.vlsolutions.swing.docking.DockKey;
import com.vlsolutions.swing.docking.Dockable;

public class ProjectTreePanel extends JPanel implements Dockable {
	private class Listener extends MouseAdapter implements
			ProjectManagerListener, TreeSelectionListener {

		@Override
		public void mousePressed(MouseEvent e) {
			if (e.getSource().equals(tree)) {
				int selRow = tree.getRowForLocation(e.getX(), e.getY());
				if ((selRow != -1) && (e.getClickCount() == 2)) {
					for (ProjectDockListener l : listenerList) {
						l.projectDoubleClicked(new ProjectPanelEvent(this,
								selectedProject, selectedNodeItem,
								selectedChildObj));
					}
				}
			}
		}

		@Override
		public void mouseReleased(MouseEvent e) {
			if (e.isPopupTrigger()||e.isControlDown()||e.getButton()==MouseEvent.BUTTON3) {
				for (ProjectDockListener l : listenerList) {
					l.projectPopupTriggered(
							new ProjectPanelEvent(this, selectedProject,
									selectedNodeItem, selectedChildObj), e
									.getComponent(), e.getX(), e.getY());
				}
			}

		}

		public void projectAdded(ProjectManager source, Project gp) {
			addProject(gp);
		}

		public void projectRemoved(ProjectManager source, Project gp, int index) {
			treeModel.removeNodeFromParent(projectMap.get(gp));
			if(tree.getPathForRow(0) != null) {
				tree.setSelectionRow(0);
			} else {
				selectedChildObj = null;
				selectedProject = null;
				selectedNodeItem = null;
				carbonMain.showPropertyDock(null, MenuProperty.GENERAL_ABOUT, null);
				for (ProjectDockListener l : listenerList) {
					l.projectSelectionChanged(new ProjectPanelEvent(this,
							selectedProject, selectedNodeItem, selectedChildObj));
				}
			}
			menuNodeMap.remove(gp);
		}

		public void valueChanged(TreeSelectionEvent e) {
			selectedTreeNode = (DefaultMutableTreeNode) tree
					.getLastSelectedPathComponent();
			selectedChildObj = null;
			selectedProject = null;
			selectedNodeItem = null;
			if (selectedTreeNode == null)
				return;
			Object nodeObj = selectedTreeNode.getUserObject();
			if (nodeObj instanceof Project) {
				selectedProject = (Project) nodeObj;
				selectedNodeItem = PROJECT;
			} else if (nodeObj instanceof MenuAdapter) {
				selectedNodeItem = (MenuAdapter) nodeObj;
				Object objPath[] = selectedTreeNode.getUserObjectPath();
				selectedProject = (Project) objPath[1];
			}
			for (ProjectDockListener l : listenerList) {
				l.projectSelectionChanged(new ProjectPanelEvent(this,
						selectedProject, selectedNodeItem, selectedChildObj));
			}
		}

		

	}

	// tree item label and icon
	class PropertiesItemRenderer extends DefaultTreeCellRenderer {
		private static final long serialVersionUID = -8268499213545145186L;


		public PropertiesItemRenderer() {

		}

		@Override
		public Component getTreeCellRendererComponent(JTree tree, Object value,
				boolean sel, boolean expanded, boolean leaf, int row,
				boolean hasFocus) {

			super.getTreeCellRendererComponent(tree, value, sel, expanded,
					leaf, row, hasFocus);
			DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
			Object obj = node.getUserObject();
			if (obj instanceof Project) {
				Project p = (Project) obj;
				setIcon(PROJECT.getIcon(IconModel.ICON16));
				setToolTipText(p.getDescription());
				File f = getProjectManager().getFile(p);
				if(f != null) {
					setText("<html><font size=3>"+p.getLabel()+"</font><br/><font color=#fe5815 size=2>["+f.getName()+"]</font></html>");
				} else {
					setText("<html><font size=-1>"+p.getLabel()+"</font></html>");
				}
				
				setFont(fontProject);
			} else if (obj instanceof MenuAdapter) {
				MenuAdapter prop = (MenuAdapter) obj;
				setIcon(prop.getIcon(IconModel.ICON16));
				setText(prop.getLabel());
				setFont(fontItem);
				setToolTipText(prop.getAccessibleDescription());
			}
			return this;
		}
	}

	private static final long serialVersionUID = 9080850952569640268L;
	private ProjectManager projectManager;
	private DockKey key = new DockKey("ProjectPanel");
	private Listener listener = new Listener();
	private ArrayList<ProjectDockListener> listenerList = new ArrayList<ProjectDockListener>();
	private HashMap<Project, DefaultMutableTreeNode> projectMap = new HashMap<Project, DefaultMutableTreeNode>();
	private DefaultMutableTreeNode rootNode;
	private Object selectedChildObj;
	private MenuAdapter selectedNodeItem;
	private Project selectedProject;
	private DefaultMutableTreeNode selectedTreeNode;
	private AbacusMain carbonMain;
	private JTree tree;
	private DefaultTreeModel treeModel;
	private Font fontProject;
	private Font fontItem;

	public ProjectTreePanel(ProjectManager gPilotManager, AbacusMain main) {
		super(new GridLayout(1, 0));
		this.projectManager = gPilotManager;
		this.projectManager.addListener(listener);
		this.carbonMain = main;
		rootNode = new DefaultMutableTreeNode("REDD Abacus SP");
		treeModel = new DefaultTreeModel(rootNode);

		tree = new JTree(treeModel);
		fontItem = tree.getFont();
		fontProject = fontItem.deriveFont(Font.BOLD, 12);
		tree.getSelectionModel().setSelectionMode(
				TreeSelectionModel.SINGLE_TREE_SELECTION);
		tree.setShowsRootHandles(true);
		tree.setRootVisible(false);
		tree.setCellRenderer(new PropertiesItemRenderer());
		tree.addTreeSelectionListener(listener);
		tree.addMouseListener(listener);
		tree.setRowHeight(-1);
		
		scrollPane = new JScrollPane(tree);
		
		add(scrollPane);

		key.setName(PROJECT_MANAGER.getLabel());
		key.setTooltip(PROJECT_MANAGER.getAccessibleDescription());
		key.setIcon(PROJECT_MANAGER.getIcon(IconModel.ICON16));
		key.setCloseEnabled(false);
		key.setAutoHideEnabled(true);
		key.setMaximizeEnabled(false);

		tree.getInputMap().put(KeyStroke.getKeyStroke("DELETE"), "delete");
		tree.getActionMap().put("delete", new AbstractAction() {
			private static final long serialVersionUID = -3884027154884332834L;

			public void actionPerformed(ActionEvent e) {
				getAbacusMain().removeProject(getSelectedProject(), (Component)e.getSource());
			}
		});
		

	}





	private void addProject(Project prj) {
		selectedProject = prj;
		DefaultMutableTreeNode prjNode = new DefaultMutableTreeNode(prj);
		treeModel.insertNodeInto(prjNode, rootNode, rootNode.getChildCount());
		projectMap.put(prj, prjNode);
		
		DefaultMutableTreeNode inputNode = addProjectChild(prjNode, PROJECT_INPUT);
		
		DefaultMutableTreeNode spatialNode = addProjectChild(inputNode, PROJECT_SPATIAL);
		DefaultMutableTreeNode economicNode = addProjectChild(inputNode, PROJECT_ECONOMIC);
		
		addProjectChild(spatialNode, PROJECT_LANDUSE);
		addProjectChild(spatialNode, PROJECT_ZONATION);
		addProjectChild(spatialNode, PROJECT_LCOVER_CHANGE);
		addProjectChild(spatialNode, PROJECT_CSTOCK);
		
		addProjectChild(economicNode, PROJECT_COSTB_UNIT);
		addProjectChild(economicNode, PROJECT_NPV);
		addProjectChild(economicNode, PROJECT_COSTB);
		
		DefaultMutableTreeNode emitNode = addProjectChild(inputNode, PROJECT_EMISSION_ADD);
		addProjectChild(emitNode, PROJECT_BG_EMISSION);
		addProjectChild(emitNode, PROJECT_MODIF_EMISSION);

		DefaultMutableTreeNode outputNode = addProjectChild(prjNode,
				PROJECT_OUTPUT);
		addProjectChild(outputNode, PROJECT_SUMMARY_EMISSION);
		addProjectChild(outputNode, PROJECT_COSTB_TOTAL);
		addProjectChild(outputNode, PROJECT_SUMMARY_OPCOST);
		addProjectChild(outputNode,	PROJECT_SUMMARY);
		addProjectChild(outputNode, PROJECT_ABATEMENT);
		
		//model
		DefaultMutableTreeNode modelNode = addProjectChild(prjNode,
				MODEL_SIMULATION);
		DefaultMutableTreeNode scenNode = addProjectChild(modelNode, MODEL_SCENARIO);
		addProjectChild(scenNode, MODEL_MAIN);
		addProjectChild(scenNode, MODEL_TPM);
		addProjectChild(scenNode, MODEL_LANDUSE_SCENARIO);
		
		DefaultMutableTreeNode dynNode = addProjectChild(scenNode, MODEL_DYNAMIC);
		addProjectChild(dynNode, MODEL_CSTOCK);
		addProjectChild(dynNode, MODEL_NPV);
		addProjectChild(dynNode, MODEL_CB);
		addProjectChild(dynNode, MODEL_BG);
		addProjectChild(dynNode, MODEL_MODIF);
		
		DefaultMutableTreeNode modoutNode = addProjectChild(modelNode, MODEL_OUTPUT);
		addProjectChild(modoutNode, MODEL_SUMMARY);
		addProjectChild(modoutNode, MODEL_LANDUSE);
		addProjectChild(modoutNode, MODEL_EMISSION);
		addProjectChild(modoutNode, MODEL_COSTB_TOTAL);
		addProjectChild(modoutNode, MODEL_OPCOST);
		
		
//		expandNode(spatialNode.getFirstChild());
//		expandNode(economicNode.getFirstChild());
//		expandNode(emitNode.getFirstChild());
//		expandNode(outputNode.getFirstChild());
		expandNode(prjNode.getFirstChild());
		tree.setSelectionPath(new TreePath(prjNode.getPath()));

	}

	HashMap<Project, HashMap<MenuAdapter, DefaultMutableTreeNode>> menuNodeMap = new HashMap<Project, HashMap<MenuAdapter,DefaultMutableTreeNode>>();
	private JScrollPane scrollPane;
	
	protected DefaultMutableTreeNode addProjectChild(
			DefaultMutableTreeNode parentNode, MenuProperty p) {
		DefaultMutableTreeNode node = new DefaultMutableTreeNode(p);
		treeModel.insertNodeInto(node, parentNode, parentNode.getChildCount());
		HashMap<MenuAdapter, DefaultMutableTreeNode> m = menuNodeMap.get(selectedProject);
		if(m == null) m = new HashMap<MenuAdapter, DefaultMutableTreeNode>();
		m.put(p, node);
		menuNodeMap.put(selectedProject, m);
		return node;
	}

	public void addProjectDockListener(ProjectDockListener l) {
		listenerList.add(l);
	}

	
	public void expandNode(TreeNode node) {
		tree.scrollPathToVisible(new TreePath(((DefaultMutableTreeNode) node)
				.getPath()));
	}

	public void expandSelectedNode() {
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) selectedTreeNode
				.getFirstChild();
		tree.scrollPathToVisible(new TreePath(node.getPath()));
	}

	public Component getComponent() {
		return this;
	}

	public DockKey getDockKey() {
		return key;
	}


	/**
	 * @return the selectedNode
	 */
	public MenuAdapter getSelectedNodeItem() {
		return selectedNodeItem;
	}

	/**
	 * @return the selectedProject
	 */
	public Project getSelectedProject() {
		return selectedProject;
	}

	/**
	 * @return the sexiDesktop
	 */
	public AbacusMain getAbacusMain() {
		return carbonMain;
	}

	protected void removeChildNode(MenuProperty p) {
		DefaultMutableTreeNode node = null;
		Enumeration<?> e = selectedTreeNode.children();
		while (e.hasMoreElements()) {
			DefaultMutableTreeNode n = (DefaultMutableTreeNode) e.nextElement();
			if (n.getUserObject().equals(p)) {
				node = n;
				break;
			}
		}
		if (node != null) {
			treeModel.removeNodeFromParent(node);
		}
	}

	public void removeListener(ProjectDockListener l) {
		listenerList.remove(l);
	}


	public ProjectManager getProjectManager() {
		return projectManager;
	}

	public void showSelectedChildNode(int idx) {
		if(idx > selectedTreeNode.getChildCount()-1)
			return;
		TreeNode c = selectedTreeNode.getChildAt(idx);
		if(c!=null)
		tree.setSelectionPath(new TreePath(((DefaultMutableTreeNode)c).getPath()));
	}
	
	public void showSelectedParentNode() {
		TreeNode c = selectedTreeNode.getParent();
		if(c==null) return;
		if(c.equals(rootNode)) return;
		
		tree.setSelectionPath(new TreePath(((DefaultMutableTreeNode)c).getPath()));
	}
	
	public void setSelection(Project p, MenuAdapter treeMenu) {
		HashMap<MenuAdapter, DefaultMutableTreeNode> m = menuNodeMap.get(p);
		if(m == null) return;
		TreeNode c = m.get(treeMenu);
		if(c!=null)
		tree.setSelectionPath(new TreePath(((DefaultMutableTreeNode)c).getPath()));
	}
	
	public void refreshTreeNode() {
		tree.repaint();
	}

}